﻿using FunasrSTT.Common.Helpers;
using Microsoft.VisualBasic;
using NAudio.CoreAudioApi;
using NAudio.Wave;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Navigation;
using Websocket.Client;

namespace FunasrSTT.Translate.Services;

public class WebSocketService
{
    private Uri serverUri;
    private static WebsocketClient client;
    private int chunk_interval = 10;//发包间隔，chunk是600ms 发包间隔就是chunk/chunk_interval为60ms
    private int[] chunk_size = new int[] { 5, 10, 5 };//数据块chunk大小，"5,10,5"=600ms "8,8,4"=480ms
    private string hotwords;
    private string recbuff = string.Empty; //接收累计缓存内容
    private string onlinebuff = string.Empty; //接收累计在线缓存内容 
    private string onlineasrmode;  //识别类型
    private int wave_buffer_collectfrequency = 16000; //音频采样率16000 Hz
    public event Action<string> OnMessageReceived; //声明语音识别内容接收事件，用于显示在界面上

    /// <summary>
    /// 客户端连接状态
    /// </summary>
    /// <returns></returns>
    public bool IsRunning { get { return client.IsRunning; } }

    /// <summary>
    /// 识别类型
    /// </summary>
    public string OnlineASRMode { get { return onlineasrmode; } set { onlineasrmode = value; } }

    /// <summary>
    /// 热词
    /// </summary>
    public string HotWords { get { return hotwords; } set { hotwords = value; } }

    public string Recbuff { get => recbuff; set => recbuff = value; }
    public string Onlinebuff { get => onlinebuff; set => onlinebuff = value; }

    /// <summary>
    /// WebSocket通信
    /// </summary>
    /// <param name="serviceIp"></param>
    /// <param name="port"></param>
    public WebSocketService(string serviceIp, int port)
    {
        serverUri = new Uri($"ws://{serviceIp}:{port}");   //你要连接的WebSocket服务器地址
        client = new WebsocketClient(serverUri);
    }

    /// <summary>
    /// 更新Uri和重启client
    /// </summary>
    /// <param name="host"></param>
    /// <param name="port"></param>
    public async Task<bool> ClientReConnect(string host, string port)
    {
        // 更新 serverUri
        serverUri = new Uri($"ws://{host}:{port}");
        if (client.IsRunning)
        {
            try
            {
                var result = await client.Stop(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "client reconnect.");
                if (result)
                {
                    client = new WebsocketClient(serverUri);
                    return await this.ClientConnect();
                }
            }
            catch (Exception ex)
            {
                client.Dispose();
            }
        }
        return false;
    }

    /// <summary>
    /// 重新连接服务器
    /// </summary>
    /// <returns></returns>
    public async Task<bool> ClientReConnect()
    {
        if (client.IsRunning)
        {
            try
            {
                var result = await client.Stop(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "client reconnect.");
                if (result)
                {
                    client = new WebsocketClient(serverUri);
                    return await this.ClientConnect();
                }
            }
            catch (Exception ex)
            {
                client.Dispose();
            }
        }
        return false;
    }

    /// <summary>
    /// 连接服务器
    /// </summary>
    /// <returns></returns>
    public async Task<bool> ClientConnect()
    {
        bool ret = false;
        string commstatus = "连接失败！";
        try
        {
            client.Name = "funasr";
            client.ReconnectTimeout = null;
            client.ReconnectionHappened.Subscribe(info =>
               Console.WriteLine($"Reconnection happened, type: {info.Type}, url: {client.Url}"));
            client.DisconnectionHappened.Subscribe(info =>
                Console.WriteLine($"Disconnection happened, type: {info.Type}"));

            client
                .MessageReceived
                .Where(msg => msg.Text != null)
                .Subscribe(msg =>
                {
                    if (msg.Text != null)
                    {
                        Rec_Message(msg.Text, client);
                    }
                });

            await client.Start();

            if (client.IsRunning)
            {
                commstatus = "连接成功！";
                ret = true;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            client.Dispose();
        }

        return ret;
    }

    /// <summary>
    /// 断开连接
    /// </summary>
    public async Task<bool> ClientDisConnect()
    {
        if (client.IsRunning)
        {
            try
            {
                return await client.Stop(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "client disconnect.");
            }
            catch (Exception ex)
            {
                client.Dispose();
            }
        }
        return false;
    }

    /// <summary>
    /// 初始发送数据函数，将模式、发包间隔、热词等内容发送给服务器
    /// </summary>
    /// <param name="asrmode"></param>
    /// <returns></returns>
    public bool ClientFirstConnOnline(string asrmode)
    {
        if (client.IsRunning)
        {
            string firstbuff = $"{{\"mode\": \"{asrmode}\", \"chunk_size\": [{chunk_size[0]},{chunk_size[1]},{chunk_size[2]}], \"chunk_interval\": {chunk_interval}, \"wav_name\": \"microphone\", \"is_speaking\": true,\"hotwords\":\"{hotwords}\"}}";
            Task.Run(() => client.Send(firstbuff));
        }
        else
        {
            client.Reconnect();//断线重连
            return false;
        }

        return true;
    }

    /// <summary>
    /// 语音实时识别函数，参数为音频数组
    /// </summary>
    /// <param name="buff"></param>
    /// <returns></returns>
    public bool ClientSendAudioFunc(byte[] buff)
    {
        if (client.IsRunning)
        {
            //发送音频数据
            int CHUNK = wave_buffer_collectfrequency / 1000 * 120 * chunk_size[1] / chunk_interval;
            for (int i = 0; i < buff.Length; i += CHUNK)
            {
                byte[] send = buff.Skip(i).Take(CHUNK).ToArray();
                Task.Run(() => client.Send(send));
                Thread.Sleep(1);
            }
        }
        else
        {
            client.Reconnect();
            return false;
        }

        return true;
    }

    /// <summary>
    /// 文件识别函数，参数为音频文件路径
    /// 文件转录， 0：发送成功；-1：文件类型不支持；-2：通信断开
    /// </summary>
    /// <param name="file_name"></param>
    /// <returns></returns>
    public void ClientSendFileFunc(string file_name)
    {
        string fileExtension = Path.GetExtension(file_name);
        fileExtension = fileExtension.Replace(".", "");
        if (!(fileExtension == "mp3" || fileExtension == "mp4" || fileExtension == "wav" || fileExtension == "pcm"))
        {
            throw new Exception("无法识别的文件格式");
        }
        if (client.IsRunning)
        {
            //语音文件采样率
            int sampleRate = 16000;
            byte[] wavBytes = File.ReadAllBytes(file_name);
            var info = WavParser.ReadWavInfo(wavBytes);
            if (info != null)
            {
                sampleRate = info.SampleRate;
            }

            if (fileExtension == "wav" || fileExtension == "pcm")
            {
                string firstbuff = $"{{\"mode\": \"offline\", \"chunk_size\": [{chunk_size[0]},{chunk_size[1]},{chunk_size[2]}], \"chunk_interval\": {chunk_interval}, \"wav_name\": \"{Path.GetFileName(file_name)}\", \"is_speaking\": true,\"hotwords\":\"{hotwords}\",\"wav_format\":\"pcm\",\"audio_fs\":{sampleRate}}}";
                Task.Run(() => client.Send(firstbuff));
                if (fileExtension == "wav")
                    SendWAVFile(file_name);
                else if (fileExtension == "pcm")
                    SendWAVFile_All(file_name);
            }
            else if (fileExtension == "mp3" || fileExtension == "mp4")
            {
                string firstbuff = $"{{\"mode\": \"offline\", \"chunk_size\": [{chunk_size[0]},{chunk_size[1]},{chunk_size[2]}], \"chunk_interval\": {chunk_interval}, \"wav_name\": \"{Path.GetFileName(file_name)}\", \"is_speaking\": true,\"hotwords\":\"{hotwords}\",\"wav_format\":\"{fileExtension}\",\"audio_fs\":{sampleRate}}}";
                Task.Run(() => client.Send(firstbuff));
                SendWAVFile_All(file_name);
            }
        }
        else
        {
            client.Reconnect();
        }
    }

    /// <summary>
    /// 发送is_speaking:false告诉服务器停止说话
    /// </summary>
    public void ClientLastConnOnline()
    {
        Task.Run(() => client.Send("{\"is_speaking\": false}"));
    }

    /// <summary>
    /// 消息接收函数，将服务器发来的消息进行解析并存储在缓存中
    /// </summary>
    /// <param name="message"></param>
    /// <param name="client"></param>
    public void Rec_Message(string message, WebsocketClient client)
    {
        if (message != null)
        {
            OnMessageReceived?.Invoke(message);//触发语音识别内容接收事件

            //try
            //{
            //    //将接收到服务器发来的消息进行json转换
            //    JsonDocument jsonDoc = JsonDocument.Parse(message);
            //    JsonElement root = jsonDoc.RootElement;
            //    //判读root中有没有mode、text和is_final
            //    if (root.TryGetProperty("mode", out JsonElement modeElement) &&
            //        root.TryGetProperty("text", out JsonElement textElement) &&
            //        root.TryGetProperty("is_final", out JsonElement isFinalElement))
            //    {
            //        string mode = modeElement.GetString();
            //        string text = textElement.GetString();
            //        bool isfinal = isFinalElement.GetBoolean();

            //        if (mode == "2pass-online" && onlineasrmode != "offline")
            //        {
            //            onlinebuff += text;
            //            OnMessageReceived?.Invoke(recbuff + onlinebuff);//触发语音识别内容接收事件
            //        }
            //        else if (mode == "2pass-offline" || mode == "offline")
            //        {
            //            recbuff += text;
            //            onlinebuff = string.Empty;
            //            OnMessageReceived?.Invoke(recbuff);
            //        }
            //        else if (mode == "online")
            //        {
            //            onlinebuff += text;
            //            OnMessageReceived?.Invoke(recbuff + onlinebuff);
            //        }
            //        if (isfinal && onlineasrmode != "offline")
            //        {
            //            recbuff = string.Empty;
            //        }
            //    }
            //}
            //catch (JsonException ex)
            //{
            //    throw new Exception("JSON 解析错误: " + ex.Message + "\n收到的消息内容为:" + message);
            //}
        }
    }

    /// <summary>
    /// 将音频文件加载为字节数组，分小块发给服务器（跳过文件头44个字节，通常处理wav文件）
    /// </summary>
    /// <param name="file_name"></param>
    private void SendWAVFile(string file_name)
    {
        byte[] getbyte = FileToByte(file_name).Skip(44).ToArray();
        List<Task> taskList = new List<Task>();
        for (int i = 0; i < getbyte.Length; i += 102400)
        {
            byte[] send = getbyte.Skip(i).Take(102400).ToArray();

            //控制最多N个线程
            if (taskList.Count(t => t.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) >= 32)
            {
                Task.WaitAny(taskList.ToArray());
                taskList = taskList.Where(t => t.Status != System.Threading.Tasks.TaskStatus.RanToCompletion).ToList();
            }
            taskList.Add(Task.Run(() => client.Send(send)));
            Thread.Sleep(5);
        }
        //等待所有线程结束
        Task.WaitAll(taskList.ToArray());

        Thread.Sleep(100);

        Task.Run(() => client.Send("{\"is_speaking\": false}"));
    }

    /// <summary>
    /// 将音频文件加载为字节数组，分大块发给服务器（不跳过文件头，通常处理非wav文件）
    /// </summary>
    /// <param name="file_name"></param>
    private void SendWAVFile_All(string file_name)
    {
        byte[] getbyte = FileToByte(file_name).ToArray();
        List<Task> taskList = new List<Task>();
        for (int i = 0; i < getbyte.Length; i += 1024000)
        {
            byte[] send = getbyte.Skip(i).Take(1024000).ToArray();
            //控制最多N个线程
            if (taskList.Count(t => t.Status != System.Threading.Tasks.TaskStatus.RanToCompletion) >= 32)
            {
                Task.WaitAny(taskList.ToArray());
                taskList = taskList.Where(t => t.Status != System.Threading.Tasks.TaskStatus.RanToCompletion).ToList();
            }
            taskList.Add(Task.Run(() => client.Send(send)));
            Thread.Sleep(5);
        }
        //等待所有线程结束
        Task.WaitAll(taskList.ToArray());

        Thread.Sleep(100);

        Task.Run(() => client.Send("{\"is_speaking\": false}"));
    }

    /// <summary>
    /// 将音频文件转为byte数组
    /// </summary>
    /// <param name="fileUrl"></param>
    /// <returns></returns>
    public byte[] FileToByte(string fileUrl)
    {
        try
        {
            using (FileStream fs = new FileStream(fileUrl, FileMode.Open, FileAccess.Read))
            {
                byte[] byteArray = new byte[fs.Length];
                fs.Read(byteArray, 0, byteArray.Length);
                return byteArray;
            }
        }
        catch
        {
            return null;
        }
    }

}


